home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue43 / alfresco / AASkpLst.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-01-24  |  11.8 KB  |  440 lines

  1. {*********************************************************}
  2. {* AASkpLst                                              *}
  3. {* Copyright (c) Julian M Bucknall 1999                  *}
  4. {* All rights reserved.                                  *}
  5. {*********************************************************}
  6. {* Skip list container                                   *}
  7. {*********************************************************}
  8.  
  9. {Note: this unit is released as freeware. In other words, you are free
  10.        to use this unit in your own applications, however I retain all
  11.        copyright to the code. JMB}
  12.  
  13. unit AASkpLst;
  14.  
  15. interface
  16.  
  17. uses
  18.   SysUtils;
  19.  
  20. {$IFOPT D+}
  21. {$DEFINE InDebugMode}
  22. {$ENDIF}
  23.  
  24. {$DEFINE UseNodeManager}
  25.  
  26. const
  27.   PageNodeCount = 30;
  28.   MaxSkipLevels = 16;
  29.  
  30. type
  31.   TaaCompareFunction = function (aItem1, aItem2 : pointer) : integer;
  32.  
  33. type
  34.   PslNode = ^TslNode;
  35.  
  36.   TslNodeArray = array [0..pred(MaxSkipLevels)] of PslNode;
  37.  
  38.   TslNode = packed record
  39.     slnData : pointer;
  40.     slnLevel: longint;
  41.     slnPrev : PslNode;
  42.     slnNext : TslNodeArray;
  43.   end;
  44.  
  45.   TaaSkipList = class
  46.     private
  47.       FCompare  : TaaCompareFunction;
  48.       FCount    : integer;
  49.       FCursor   : PslNode;
  50.       FHead     : PslNode;
  51.       FMaxLevel : integer;
  52.       FTail     : PslNode;
  53.     protected
  54.       function slSearchPrim(aItem : pointer;
  55.                         var aBeforeNodes : TslNodeArray) : boolean;
  56.     public
  57.       constructor Create(aCompare : TaaCompareFunction);
  58.       destructor Destroy; override;
  59.  
  60.       procedure Insert(aItem : pointer);
  61.       function Delete : pointer;
  62.  
  63.       function IsAfterLast : boolean;
  64.       function IsBeforeFirst : boolean;
  65.       procedure MoveAfterLast;
  66.       procedure MoveBeforeFirst;
  67.  
  68.       function MoveNext : boolean;
  69.       function MovePrior : boolean;
  70.  
  71.       procedure Clear;
  72.       function Examine : pointer;
  73.  
  74.       function Search(aItem : pointer) : boolean;
  75.  
  76.       {$IFDEF InDebugMode}
  77.       procedure Print;
  78.       {$ENDIF}
  79.  
  80.       property Count : integer read FCount;
  81.       property MaxLevel : integer read FMaxLevel;
  82.   end;
  83.  
  84.  
  85. implementation
  86.  
  87. {===SkipListNodeManager==============================================}
  88. const
  89.   NodeSize : array [0..pred(MaxSkipLevels)] of integer =
  90.              (16, 20, 24, 28, 32, 36, 40, 44,
  91.               48, 52, 56, 60, 64, 68, 72, 76);
  92. type
  93.   PslnmPage = ^TslnmPage;
  94.   TslnmPage = packed record
  95.     slnmpNext  : PslnmPage;
  96.     slnmpSize  : longint;
  97.     slnmpNodes : TByteArray;
  98.   end;
  99. {--------}
  100. var
  101.   slnmFreeList : TslNodeArray; {ie, a free list per node size}
  102.   slnmPageList : PslnmPage;
  103. {--------}
  104. procedure slnmFreeNode(aNode : PslNode; aLevel : integer);
  105. begin
  106.   {$IFDEF UseNodeManager}
  107.   {add the node to the top of the correct free list}
  108.   aNode^.slnNext[0] := slnmFreeList[aLevel];
  109.   slnmFreeList[aLevel] := aNode;
  110.   {$ELSE}
  111.   FreeMem(aNode, NodeSize[aLevel]);
  112.   {$ENDIF}
  113. end;
  114. {--------}
  115. procedure slnmAllocPage(aLevel : integer);
  116. var
  117.   NewPage : PslnmPage;
  118.   i       : integer;
  119.   PageSize: integer;
  120.   Offset  : integer;
  121. begin
  122.   {get a new page}
  123.   PageSize := sizeof(pointer) + {the slnmpNext field}
  124.               sizeof(longint) + {the slnmpSize field}
  125.               (PageNodeCount * NodeSize[aLevel]); {the nodes}
  126.   GetMem(NewPage, PageSize);
  127.   NewPage^.slnmpSize := PageSize;
  128.   {add it to the current list of pages}
  129.   NewPage^.slnmpNext := slnmPageList;
  130.   slnmPageList := NewPage;
  131.   {add all the nodes on the page to the free list}
  132.   Offset := 0;
  133.   for i := 0 to pred(PageNodeCount) do begin
  134.     slnmFreeNode(@NewPage^.slnmpNodes[Offset], aLevel);
  135.     inc(Offset, NodeSize[aLevel]);
  136.   end;
  137. end;
  138. {--------}
  139. function slnmAllocNode(aLevel : integer) : PslNode;
  140. begin
  141.   {$IFDEF UseNodeManager}
  142.   {if the free list is empty, allocate a new page of nodes}
  143.   if (slnmFreeList[aLevel] = nil) then
  144.     slnmAllocPage(aLevel);
  145.   {return the first node on the free list}
  146.   Result := slnmFreeList[aLevel];
  147.   slnmFreeList[aLevel] := Result^.slnNext[0];
  148.   {$ELSE}
  149.   GetMem(Result, NodeSize[aLevel]);
  150.   {$ENDIF}
  151.   Result^.slnLevel := aLevel;
  152. end;
  153. {====================================================================}
  154.  
  155.  
  156. {===TaaSkipList======================================================}
  157. constructor TaaSkipList.Create(aCompare : TaaCompareFunction);
  158. var
  159.   i : integer;
  160. begin
  161.   inherited Create;
  162.   {allocate a head node}
  163.   FHead := slnmAllocNode(pred(MaxSkipLevels));
  164.   FHead^.slnData := nil;
  165.   {allocate a tail node}
  166.   FTail := slnmAllocNode(0);
  167.   FTail^.slnData := nil;
  168.   {point the head and tail pointers to each other}
  169.   for i := 0 to pred(MaxSkipLevels) do begin
  170.     FHead^.slnNext[i] := FTail;
  171.     FTail^.slnNext[i] := nil;
  172.   end;
  173.   FHead^.slnPrev := nil;
  174.   FTail^.slnPrev := FHead;
  175.   {set the cursor to the head node}
  176.   FCursor := FHead;
  177.   {save the compare function}
  178.   FCompare := aCompare;
  179. end;
  180. {--------}
  181. destructor TaaSkipList.Destroy;
  182. begin
  183.   Clear;
  184.   slnmFreeNode(FHead, FHead^.slnLevel);
  185.   slnmFreeNode(FTail, FTail^.slnLevel);
  186.   inherited Destroy;
  187. end;
  188. {--------}
  189. procedure TaaSkipList.Clear;
  190. var
  191.   Temp : PslNode;
  192. begin
  193.   Temp := FHead^.slnNext[0];
  194.   while (Temp <> nil) do begin
  195.     FHead^.slnNext[0] := Temp^.slnNext[0];
  196.     slnmFreeNode(Temp, Temp^.slnLevel);
  197.     Temp := FHead^.slnNext[0];
  198.   end;
  199.   FCount := 0;
  200. end;
  201. {--------}
  202. function TaaSkipList.Delete : pointer;
  203. var
  204.   i, Level    : integer;
  205.   Temp        : PslNode;
  206.   BeforeNodes : TslNodeArray;
  207. begin
  208.   {we can't delete at the head or tail}
  209.   if (FCursor = FHead) or (FCursor = FTail) then
  210.     raise Exception.Create('TaaSkipList.Delete: cannot delete - cursor is not on an item');
  211.   {search for the item and create the BeforeNodes array}
  212.   if not slSearchPrim(FCursor^.slnData, BeforeNodes) then
  213.     raise Exception.Create('TaaSkipList.Delete: item is missing');
  214.   {the only valid before nodes are from the skip list's maximum level
  215.    down to this node's level; we need to get the before nodes for the
  216.    others}
  217.   Level := FCursor^.slnLevel;
  218.   if (Level > 0) then begin
  219.     for i := pred(Level) downto 0 do begin
  220.       BeforeNodes[i] := BeforeNodes[i+1];
  221.       while (BeforeNodes[i]^.slnNext[i] <> FCursor) do
  222.         BeforeNodes[i] := BeforeNodes[i]^.slnNext[i];
  223.     end;
  224.   end;
  225.   {patch up the links on level 0 - doubly linked list}
  226.   BeforeNodes[0]^.slnNext[0] := FCursor^.slnNext[0];
  227.   FCursor^.slnNext[0]^.slnPrev := BeforeNodes[0];
  228.   {patch up the links on the other levels - all singly linked lists}
  229.   for i := 1 to Level do begin
  230.     BeforeNodes[i].slnNext[i] := FCursor^.slnNext[i];
  231.   end;
  232.   {reset cursor, dispose of the node}
  233.   Result := FCursor^.slnData;
  234.   Temp := FCursor;
  235.   FCursor := FCursor^.slnNext[0];
  236.   slnmFreeNode(Temp, Level);
  237.   {we now have one less node in the skip list}
  238.   dec(FCount);
  239. end;
  240. {--------}
  241. function TaaSkipList.Examine : pointer;
  242. begin
  243.   {return the data part of the cursor}
  244.   Result := FCursor^.slnData;
  245. end;
  246. {--------}
  247. procedure TaaSkipList.Insert(aItem : pointer);
  248. var
  249.   i, Level    : integer;
  250.   NewNode     : PslNode;
  251.   BeforeNodes : TslNodeArray;
  252. begin
  253.   {search for the item and create the BeforeNodes array}
  254.   if slSearchPrim(aItem, BeforeNodes) then
  255.     raise Exception.Create('TaaSkipList.Insert: duplicate item');
  256.   {calculate the level for the new node}
  257.   Level := 0;
  258.   while (Level <= MaxLevel) and (Random < 0.25) do
  259.     inc(Level);
  260.   {if we've gone beyond the maximum level, save it}
  261.   if (Level > MaxLevel) then
  262.     inc(FMaxLevel);
  263.   {allocate the new node}
  264.   NewNode := slnmAllocNode(Level);
  265.   NewNode^.slnData := aItem;
  266.   {patch up the links on level 0 - a doubly linked list}
  267.   NewNode^.slnPrev := BeforeNodes[0];
  268.   NewNode^.slnNext[0] := BeforeNodes[0].slnNext[0];
  269.   BeforeNodes[0].slnNext[0] := NewNode;
  270.   NewNode^.slnNext[0]^.slnPrev := NewNode;
  271.   {patch up the links on the other levels - all singly linked lists}
  272.   for i := 1 to Level do begin
  273.     NewNode^.slnNext[i] := BeforeNodes[i].slnNext[i];
  274.     BeforeNodes[i].slnNext[i] := NewNode;
  275.   end;
  276.   {we now have one more node in the skip list}
  277.   inc(FCount);
  278. end;
  279. {--------}
  280. function TaaSkipList.IsAfterLast : boolean;
  281. begin
  282.   Result := FCursor = FTail;
  283. end;
  284. {--------}
  285. function TaaSkipList.IsBeforeFirst : boolean;
  286. begin
  287.   Result := FCursor = FHead;
  288. end;
  289. {--------}
  290. procedure TaaSkipList.MoveAfterLast;
  291. begin
  292.   {set the cursor to the tail node}
  293.   FCursor := FTail;
  294. end;
  295. {--------}
  296. procedure TaaSkipList.MoveBeforeFirst;
  297. begin
  298.   {set the cursor to the head node}
  299.   FCursor := FHead;
  300. end;
  301. {--------}
  302. function TaaSkipList.MoveNext : boolean;
  303. begin
  304.   {advance the cursor to its own next pointer}
  305.   if (FCursor = FTail) then
  306.     Result := false
  307.   else begin
  308.     FCursor := FCursor^.slnNext[0];
  309.     Result := true;
  310.   end;
  311. end;
  312. {--------}
  313. function TaaSkipList.MovePrior : boolean;
  314. begin
  315.   {advance the cursor to its own previous pointer}
  316.   if (FCursor = FHead) then
  317.     Result := false
  318.   else begin
  319.     FCursor := FCursor^.slnPrev;
  320.     Result := true;
  321.   end;
  322. end;
  323. {--------}
  324. {$IFDEF InDebugMode}
  325. procedure TaaSkipList.Print;
  326. var
  327.   BeforeNodes : TslNodeArray;
  328.   i           : integer;
  329.   Temp        : PslNode;
  330.   TempLevel   : integer;
  331. begin
  332.   {set the BeforeNodes array to point to the head node}
  333.   for i := 0 to pred(MaxSkipLevels) do
  334.     BeforeNodes[i] := FHead;
  335.   Temp := FHead;
  336.   TempLevel := Temp^.slnLevel;
  337.   for i := 0 to TempLevel do
  338.     write('*');
  339.   writeln;
  340.   Temp := Temp^.slnNext[0];
  341.   while Temp <> FTail do begin
  342.     TempLevel := Temp^.slnLevel;
  343.     if (BeforeNodes[TempLevel]^.slnNext[TempLevel] <> Temp) then begin
  344.       writeln('---Wrong pointer from before');
  345.       readln;
  346.     end;
  347.     for i := 0 to TempLevel do
  348.       BeforeNodes[i] := Temp;
  349.     for i := 0 to TempLevel do
  350.       write('*');
  351.     writeln;
  352.     Temp := Temp^.slnNext[0];
  353.   end;
  354. end;
  355. {$ENDIF}
  356. {--------}
  357. function TaaSkipList.Search(aItem : pointer) : boolean;
  358. var
  359.   BeforeNodes : TslNodeArray;
  360. begin
  361.   Result := slSearchPrim(aItem, BeforeNodes);
  362. end;
  363. {--------}
  364. function TaaSkipList.slSearchPrim(aItem : pointer;
  365.                               var aBeforeNodes : TslNodeArray) : boolean;
  366. var
  367.   Level  : integer;
  368.   Walker : PslNode;
  369.   Temp   : PslNode;
  370.   CmpResult : integer;
  371. begin
  372.   {set the BeforeNodes array to point to the head node}
  373.   for Level := 0 to pred(MaxSkipLevels) do
  374.     aBeforeNodes[Level] := FHead;
  375.   {initialize}
  376.   Walker := FHead;
  377.   Level := MaxLevel;
  378.   {start zeroing in on the item we want}
  379.   while (Level >= 0) do begin
  380.     Temp := Walker^.slnNext[Level];
  381.     if (Temp = FTail) then
  382.       {pretend that the tail's data is greater than our item}
  383.       CmpResult := 1
  384.     else
  385.       {compare the next node's data with our item}
  386.       CmpResult := FCompare(Temp^.slnData, aItem);
  387.     if (CmpResult = 0) then begin
  388.       {if equal then we found the item}
  389.       aBeforeNodes[Level] := Walker;
  390.       FCursor := Temp;
  391.       Result := true;
  392.       Exit;
  393.     end;
  394.     if (CmpResult < 0) then begin
  395.       {if less than, then advance the walker node}
  396.       Walker := Temp;
  397.     end
  398.     else begin
  399.       {if greater than, save the before node, drop down a level}
  400.       aBeforeNodes[Level] := Walker;
  401.       dec(Level);
  402.     end;
  403.   end;
  404.   {if we reach this point, the item is not in the skip list}
  405.   Result := false;
  406. end;
  407. {====================================================================}
  408.  
  409.  
  410. procedure FinalizeUnit;
  411. var
  412.   STemp : PslnmPage;
  413. begin
  414.   {destroy all the single node pages}
  415.   STemp := slnmPageList;
  416.   while (STemp <> nil) do begin
  417.     slnmPageList := STemp^.slnmpNext;
  418.     FreeMem(STemp, STemp^.slnmpSize);
  419.     STemp := slnmPageList;
  420.   end;
  421. end;
  422.  
  423. procedure InitializeUnit;
  424. var
  425.   i : integer;
  426. begin
  427.   {set all global lists to nil}
  428.   for i := 0 to pred(MaxSkipLevels) do
  429.     slnmFreeList[i] := nil;
  430.   slnmPageList := nil;
  431. end;
  432.  
  433. initialization
  434.   InitializeUnit;
  435.  
  436. finalization
  437.   FinalizeUnit;
  438.  
  439. end.
  440.